#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <vector>
#include <queue>
#include <atomic>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <functional>
#include <unordered_map>
#include <thread>
#include <future>
#include <iostream>

const int TASK_MAX_THRESHHOLD = INT32_MAX;
const int THREAD_MAX_THRESHHOLD = 16;
const int THREAD_MAX_IDLE_TIME = 60;

// 数量固定，数量可增长
enum class PoolMode {
    MODE_FIXED,
    MODE_CACHED,
};

// 线程
class Thread {
public:
    using ThreadFunction = std::function<void(int)>;

    Thread(ThreadFunction func)
        : func_(func)
        , threadId_(generateId_++)
    {

    }

    ~Thread() = default;

    // 启动线程执行线程函数
    void start()
    {
        std::thread t(func_, threadId_);
        t.detach();
    }

    // 获取线程id 
    int getThreadId() const
    {
        return threadId_;
    }          
private:
    ThreadFunction func_;           // 线程函数
    static int generateId_;         // 自定义编号
    int threadId_;                  // 线程id
};

int Thread::generateId_  = 0;

// 线程池
class ThreadPool {
public:
    ThreadPool() 
        : poolMode_(PoolMode::MODE_FIXED)
        , initThreadSize_(0)
        , taskSize_(0)
        , taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
        , isRunning_(false)
        , idleThreadSize_(0)
        , threadSizeThreshHold_(THREAD_MAX_THRESHHOLD)
        , curThreadSize_(0)
    {

    }

    ~ThreadPool()
    {
        isRunning_ = false;

        // 等待线程池所有线程返回 阻塞 & 正在执行任务
        std::unique_lock<std::mutex> lock(taskMtx_);
        notEmpty_.notify_all();
        exit_.wait(lock, [&]()->bool {return threads_.size() == 0;});
    }

    // 设置工作模式
    void setMode(PoolMode mode)
    {
        if(checkRunningState())
            return;
        poolMode_ = mode;
    }

    // 设置任务上限
    void setTaskQueMaxThreshHold(int size)
    {
        if(checkRunningState())
            return;
        taskQueMaxThreshHold_ = size;
    }

    // 设置cached线程上限
    void setThreadSizeThreshHold(int size)
    {
        if(checkRunningState())
            return;
        if(poolMode_ == PoolMode::MODE_CACHED)
            threadSizeThreshHold_ = size;
    }
    
    // 开启线程池
    void start(size_t initThreadSize = std::thread::hardware_concurrency())
    {
        isRunning_ = true;
    
        // 创建线程对象
        initThreadSize_ = initThreadSize;
        for(int i = 0; i < initThreadSize_; ++i) {
            auto thread = std::make_unique<Thread>(
                std::bind(&ThreadPool::threadFunction, this, std::placeholders::_1));
            int threadId = thread->getThreadId();
            threads_.emplace(threadId, std::move(thread));
            curThreadSize_++;
        }
    
        // 启动所有线程执行线程函数
        for(int i = 0; i < initThreadSize_; ++i) {
            threads_[i]->start();
            idleThreadSize_++;
        }
    
    }

    // 提交任务，使用可变参模版编程
    // 函数返回值为future，future<>中需要一个类型
    template <typename Func, typename... Args>
    auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>
    {
        // 获取返回值类型
        using rType = decltype(func(args...));
        // 打包任务
        auto task = std::make_shared<std::packaged_task<rType()>>(
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...));
        std::future<rType> result = task->get_future();
        
        std::unique_lock<std::mutex> lock(taskMtx_);
        // 提交任务超时
        if(!notFull_.wait_for(lock, std::chrono::seconds(1), 
            [&]()->bool { return taskQue_.size() < taskQueMaxThreshHold_;}))
        {
            std::cout << "taskQueue is full, submit Task fail..." << std::endl;
            /*
            任务提交失败时，设置的任务`packaged_task`并没有被执行，因此在获取返回值时就会出错
            没有设置与`std::future`相关联的`std::promise`，导致future无法获取结果
            */
            auto task = std::make_shared<std::packaged_task<rType()>>(
                []()->rType {return rType();});
            // 确保任务的执行
            (*task)();
            return task->get_future();
        }

        // 提交任务
        taskQue_.emplace([task](){(*task)();});
        taskSize_++;
        notEmpty_.notify_all();

        // cache模式， 在任务小而快的情景下根据任务数量和空闲线程数量判断是否创建新的线程
        if(poolMode_ == PoolMode::MODE_CACHED 
            && taskSize_ > idleThreadSize_ && curThreadSize_ < threadSizeThreshHold_) 
        {
            std::cout << " 线程被创建! " << std::endl;
            auto thread = std::make_unique<Thread>(
                std::bind(&ThreadPool::threadFunction, this, std::placeholders::_1));
            int threadId = thread->getThreadId();
            threads_.emplace(threadId, std::move(thread));

            // 启动线程并修改线程池状态
            threads_[threadId]->start();
            curThreadSize_++;
            idleThreadSize_++;
        }

        return result;
    }
    

    
    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;
private:
    // 线程函数
    void threadFunction(int threadId)                           
    {
        // 获取上次执行任务的时间
        auto lastTime = std::chrono::high_resolution_clock().now();
    
        // 线程不断获取任务并且执行
        while(true) 
        {
            // 获取任务
            Task task;
            {
                std::unique_lock<std::mutex> lock(taskMtx_);
                std::cout << " 线程 : " << std::this_thread::get_id() << " 尝试获取任务... " << std::endl;
    
                // 每秒返回一次，需要判断空闲时间
                while(taskQue_.size() == 0) 
                {
                    // 线程池回收
                    if(!isRunning_) 
                    {
                        threads_.erase(threadId);
                        std::cout << " 线程 " << std::this_thread::get_id() << " 因线程池关闭被回收! " << std::endl;
                        exit_.notify_all();
                        return;
                    }
    
                    // cache模式下，如果创建了过多的线程，需要将多余的线程进行回收
                    // 空闲60s以上的线程则对线程进行回收
                    if(poolMode_ == PoolMode::MODE_CACHED) 
                    {
                        // 条件变量超时返回的
                        if(std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
                        {   
                            // 获取当前时间，计算差值是否超过60s
                            auto nowTime = std::chrono::high_resolution_clock().now();
                            auto durTime = std::chrono::duration_cast<std::chrono::seconds>(nowTime - lastTime);
                            // 超时回收线程
                            if(durTime.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
                            {
                                threads_.erase(threadId);
                                curThreadSize_--;
                                idleThreadSize_--;
                                    
                                std::cout << " 线程 " << std::this_thread::get_id() << " 被回收! " << std::endl;
                                return;
                            }
                        }
                    }
                    else
                    {
                        notEmpty_.wait(lock);
                    }
                       
                }
    
                idleThreadSize_--;
                std::cout << " 线程 : " << std::this_thread::get_id() << " 获取任务成功... " << std::endl;
                task = taskQue_.front();
                taskQue_.pop();
                taskSize_--;
    
                if(taskQue_.size() > 0) 
                    notEmpty_.notify_all();
                notFull_.notify_all();
            }
    
            // 执行任务
            if(task != nullptr) {
                task();
            }
    
            idleThreadSize_++;
            // 更新线程执行完任务的时间
            lastTime = std::chrono::high_resolution_clock().now();
        }
    }

    // 检查线程池运行状态
    bool checkRunningState() const
    {
        return isRunning_;
    }                             
private:    
    PoolMode poolMode_;                                         // 工作模式

    std::unordered_map<int, std::unique_ptr<Thread>> threads_;  // 线程列表
    size_t initThreadSize_;                                     // 初始线程数

    using Task = std::function<void()>;
    std::queue<Task> taskQue_;                                  // 任务队列
    std::atomic_uint taskSize_;                                 // 任务个数
    int taskQueMaxThreshHold_;                                  // 最大任务数

    std::mutex taskMtx_;
    std::condition_variable notEmpty_;
    std::condition_variable notFull_;
    std::condition_variable exit_;

    std::atomic_bool isRunning_;                                // 线程池运行状态
    std::atomic_int idleThreadSize_;                            // 空闲线程数
    int threadSizeThreshHold_;                                  // 最大线程数
    std::atomic_int curThreadSize_;                             // 当前的线程数
};

#endif


/*
taskQue_.emplace([&](){(*task)();});terminate called after throwing an instance of 'std::future_error'，在设置future出错
之前的代码在提交任务时使用了[&]捕获，这意味着lambda通过引用捕获所有外部变量。而task是一个局部变量，在submitTask函数中定义的std::shared_ptr。
当lambda被放入任务队列taskQue_时，这个lambda的执行可能会延迟，尤其是在多线程环境下。当submitTask函数返回后，局部变量task会离开作用域，其引用
计数减少。如果此时任务队列中的lambda尚未执行，而task的引用计数变为0，那么task指向的对象会被销毁。当线程从队列中取出任务执行时，解引用task就会访
问一个已经销毁的对象，导致未定义行为，比如段错误或future错误。当用户将捕获改为[task]时，是通过值捕获的方式捕获了shared_ptr。这会增加shared_ptr
的引用计数，确保即使submitTask函数返回，task指向的对象仍然存在，直到lambda执行完毕并释放其持有的shared_ptr。这样就避免了悬空引用的问题，保证了
任务执行时对象的有效性。
*/